package com.apes.scm.settle.settle.service;

import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.generator.service.SequenceService;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.soe.metadata.BusinessAttributeValue;
import com.apes.framework.plugin.soe.store.database.repository.BusinessAttributeValueRepository;
import com.apes.framework.util.Arith;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.repository.InventoryBatchRepository;
import com.apes.scm.account.ledger.model.Ledger;
import com.apes.scm.account.ledger.repository.LedgerRepository;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.contract.repository.ContractRepository;
import com.apes.scm.income.model.IncomeItem;
import com.apes.scm.income.repository.IncomeItemRepository;
import com.apes.scm.invoice.service.InvoiceService;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.party.model.Party;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.settle.payment.model.PaymentRegister;
import com.apes.scm.settle.payment.model.PaymentRegisterItem;
import com.apes.scm.settle.payment.service.PaymentRegisterService;
import com.apes.scm.settle.settle.model.*;
import com.apes.scm.settle.settle.respository.SettleItemRepository;
import com.apes.scm.settle.settle.respository.SettleRepository;
import com.apes.scm.settle.settle.respository.TaxBillItemRepository;
import com.apes.scm.settle.settle.respository.TaxBillRepository;
import com.apestech.framework.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/*
 * 采购结算
 * */
@Service("settleService")
public class SettleService extends DomainService {

    @Autowired
    SettleRepository settleRepository;

    @Autowired
    SettleItemRepository settleItemRepository;

    @Autowired
    InvoiceService invoiceService;

    @Autowired
    LedgerService ledgerService;

    @Autowired
    TaxBillRepository taxBillRepository;

    @Autowired
    TaxBillItemRepository taxBillItemRepository;

    @Autowired
    PartyRoleRepository partyRoleRepository;

    @Autowired
    private BusinessAttributeValueRepository businessAttributeValueRepository;

    @Autowired
    private PaymentRegisterService paymentRegisterService;


    public Settle create(SimpleRequest simpleRequest) {
        Settle settle = simpleRequest.getO(Settle.class);
        String id = SpringManager.getBean(SequenceService.class).getId(settle.CONST_TABLE_NAME);
        settle.setId(id);
        settle.setCreator(settle.getOperator());
        settle = save(settle);
        if (Objects.isNull(settle) || settle.getSettleItems().isEmpty()) throw new RuntimeException("结算单明细数据有误请检查");
        return settle;
    }

    public Settle findSupplier(SimpleRequest simpleRequest) {
        Settle settle = simpleRequest.getO(Settle.class);
        Settle one = settleRepository.findOne(settle.getId());
        try{
            if(Objects.isNull(one) || !"Supplier".equals(one.getSettleType())) return null;
        }catch (Exception e){

        }
        return one;
    }

    public Settle findCustomer(SimpleRequest simpleRequest) {
        Settle settle = simpleRequest.getO(Settle.class);
        Settle one = null;
        try{
             one = settleRepository.findOne(settle.getId());
            if(Objects.isNull(one) || !"Customer".equals(one.getSettleType())) return null;
        }catch (Exception e){

        }
        return one;
    }

    public Settle createSettleSimple(Settle settle) {
        settle.setState("sent");
        settle.setInvoiceState("pending");
        settle.setCreator(settle.getCreator());
        settle.setCreateDate(new Date());
        settle.setSettleType("Customer");
        Settle settle1 = settleRepository.saveAndFlush(settle);
        approveSettleSimple(settle1);

        return settle;
    }

    private void approveSettleSimple(Settle settle) {
        TaxBill taxBill = new TaxBill();
        taxBill.setCreator(settle.getOperator());
        taxBill.setInnerBusinessType(TaxBill.class.getSimpleName());
        taxBill.setPartner(settle.getPartner());
        taxBill.setCompany(settle.getCompany());
        taxBill.setCooperation(settle.getCooperation());
        taxBill.setDept(settle.getDept());
        taxBill.setState("sent");
        taxBill.setSettle(settle);

        taxBill.setTaxBillItems(getTaxBillItems(settle));
        taxBill.computeAmt();   // 计算相关金额字段
        if (taxBill.getTaxBillItems().stream().noneMatch(taxBillItem -> taxBillItem.getType().getCode().equals("return"))) {
            if (Arith.compare(taxBill.getAmtTotal(), settle.getSettleAmount(), 2) != 0) {
                throw new RuntimeException("税票金额总额不等于本期结算的结算总额请核对！！！");
            }
            if (Arith.compare(taxBill.getRebateAmount(), settle.getRebateAmount(), 2) != 0) {
                throw new RuntimeException("发票折扣总计不等于返利金额！");
            }

            // 允许有 0.5 的误差，并且记录误差值
            settle.setSettleTaxDiff(Arith.round(Math.abs(taxBill.getAmtTax() - settle.getSettleTax()), 2));
            if (settle.getSettleTaxDiff() > 0.5) {
                throw new RuntimeException("税金总额不等于本期结算的结算税金总额请核对！！！");
            }

        }
        taxBill = taxBillRepository.saveAndFlush(taxBill);
        settle.setTaxBill(taxBill);
        settle.setPaymentAmount(taxBill.getAmtTotal());
        settle.setState("approve");
        settle.setApproveDate(new Date());
        settle.setApproveUID(partyRoleRepository.findOne(settle.getCreator()));

        settleRepository.saveAndFlush(settle);
        executiveSettleSimple(settle);
    }

    private void executiveSettleSimple(Settle settle) {
        settle.setState("executive");
        settle.setExecutiveDate(new Date());
        settle.setExecutiveUID(settle.getApproveUID());

        // 发票登记单写审核人、审核时间
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", settle.getId()));
        if (!CollectionUtils.isEmpty(taxBills)){
            taxBills.forEach(item -> {
                item.setApproveDate(new Date());
                item.setApproveUID(settle.getApproveUID());
                item.setState("approve");
                taxBillRepository.saveAndFlush(item);
            });
        }
        // TODO 税票登记修改
//        TaxBill taxBill = settle.getTaxBill();
//        taxBill.setApproveDate(new Date());
//        taxBill.setApproveUID(settle.getApproveUID());
//        taxBill.setState("approve");

        // 返利明细回写
        settle.getSettleRebates().forEach(
                settleRebate -> {
                    IncomeItem incomeItem = settleRebate.getIncomeItem();
                    // 减少冻结金额
                    incomeItem.changeFreezeAmount(-1 * settleRebate.getActualAmount());
                    // 增加收款金额
                    incomeItem.changeRecoveryAmount(settleRebate.getActualAmount());
                });
        settleRepository.saveAndFlush(settle);

        PaymentRegister paymentRegister = getPaymentRegister(settle);
        paymentRegisterService.createSettleSimple(paymentRegister);
    }

    private PaymentRegister getPaymentRegister(Settle settle) {
        PaymentRegister paymentRegister = new PaymentRegister();
        paymentRegister.setDept(settle.getDept());
        paymentRegister.setCooperation(settle.getCooperation());
        paymentRegister.setAmount(settle.getSettleAmount());
        paymentRegister.setPartner(settle.getPartner());
        paymentRegister.setCompany(settle.getCompany());
        paymentRegister.setType("customer");
        paymentRegister.setCreator(settle.getCreator());
        paymentRegister.setCreateDate(settle.getCreateDate());
        Set<PaymentRegisterItem> paymentRegisterItems = new HashSet<>();
        settle.getSettleItems().forEach(v -> {
            PaymentRegisterItem paymentRegisterItem = new PaymentRegisterItem();
            paymentRegisterItem.setOrigin(v.getSettle().getOrigin());
            paymentRegisterItem.setOriginBusinessType("Settle");
            paymentRegisterItem.setTaxBillItem((TaxBillItem) v.getSettle().getTaxBill().getTaxBillItems().toArray()[0]);
            paymentRegisterItem.setAmount(v.getAmount());
            paymentRegisterItem.setPayableAmount(v.getAmount());

            paymentRegisterItems.add(paymentRegisterItem);
        });
        paymentRegister.setPaymentRegisterItem(paymentRegisterItems);
        return paymentRegister;
    }

    private Set<TaxBillItem> getTaxBillItems(Settle settle) {
        Set<TaxBillItem> taxBillItems = new HashSet<>();
        TaxBillItem taxBillItem = new TaxBillItem();
        taxBillItem.setAmount(settle.getSettleAmount());//结算金额
        taxBillItem.setType(businessAttributeValueRepository.getOne("TaxBillItem.type.not"));//无税
        taxBillItem.setRebateAmount(settle.getSettleItems().stream().mapToDouble(SettleItem::getDiscount).sum());//折扣总额
        taxBillItem.setTaxAmount(settle.getSettleItems().stream().mapToDouble(SettleItem::getAmtTax).sum());
        taxBillItem.setTaxReturnDate(new Date());

        taxBillItems.add(taxBillItem);
        return taxBillItems;
    }

    /*
     * 新增 不保存发票信息
     * */
    public Settle save(Settle settle) {
        settle.setState("sent");
        settle.setReceiptState("not");
        settle.setInvoiceState("pending");
        // 初始化金额（明细重新结算）
        settle.initAmt();
        // 结算返利
        processSettleRebates(settle);
        // 结算明细
        processSettleItems(settle);
        //计算采购金额
        settle.calculatePurchaseAmount();
        settle.setSettleType(settle.getPartner().getRole().getId());
        if (Objects.isNull(settle.getDept())) {
            Dept dept = SpringManager.getBean(DeptRepository.class)
                    .findAll(JpaDsl.toCriteriaByEq("company", settle.getCompany(), "deptType.id", "000007"))
                    .stream().findFirst().orElseThrow(() -> new RuntimeException(String.format("获取公司 [%s] 的销售部门失败", settle.getCompany().getId())));
            settle.setDept(dept);
        }

        Settle result = createSettle(settle);
        // 账款项变化金额为： 结算 + 返利
        // 减少未清,增加冻结
        transaction(result, "current_unsettled", -1 * (result.getSettleAmount() + result.getRebateAmount()), "current_freeze", result.getSettleAmount() + result.getRebateAmount());
        // * 不能直接使用主表返利金额,明细折扣已经扣除返利
        // 存在使用返利明细
        double rebatesAmount = getSettleRebatesAmount(result);
        if (rebatesAmount > 0) {
            // 减少返利未清，增加返利冻结
            transaction(result, "discount_unsettled", -1 * rebatesAmount, "discount_freeze", rebatesAmount);
        }
        return result;
    }

    /*
     * 处理结算返利相关逻辑
     * */
    private void processSettleRebates(Settle settle) {
        String id = settle.getId();
        // 结算返利
        List<SettleRebate> settleRebates = settle.getSettleRebates();
        SettleRebate settleRebate;
        IncomeItem incomeItem;
        int size = settleRebates.size();
        for (int i = 0; i < size; i++) {
            settleRebate = settleRebates.get(i);
            settleRebate.setId(new StringBuffer(id).append(".").append(i).toString());
            // 计算税金、更新主表返利金额
            settleRebate.computeAmt();
            incomeItem = settleRebate.getIncomeItem();
            // 增加收入单冻结金额,更新是否完成
            incomeItem.changeFreezeAmount(settleRebate.getActualAmount());
            incomeItem.updateDone();
        }
    }

    /*
     * 处理结算明细相关逻辑
     * */
    private void processSettleItems(Settle settle) {
        String id = settle.getId();
        //结算明细数据过多,这里不取系统序列id
        List<SettleItem> settleItems = settle.getSettleItems();
        int size = settleItems.size();
        IntStream.range(0, size).forEach(
                (i) -> {
                    SettleItem item = settleItems.get(i);
                    String itemId = item.getId();
                    if (itemId == null) {
                        item.setId(new StringBuffer(id).append(".").append(i).toString());
                    }
                    // 重新计算入库、返厂、变价、返利、总计金额
                    item.computeAmt();
                    // 增加结算表冻结数量
                    invoiceService.addInvoiceItemFreezeQty(item.getInvoiceItem(), item.getActualQty());
                }
        );
    }

    /*
     * 更新结算单信息
     * */
    private Settle createSettle(Settle settle) {
        // 一对多双向关联, 多方可能为空则更新得重新赋值
        /*if(settle.getSettleRebates().isEmpty()) {
            settle.setSettleRebates(new ArrayList<>());
        }*/
        return settleRepository.saveAndFlush(settle);
    }


    /*
     * 更新
     * */
    public Settle update(SimpleRequest simpleRequest) {
        Settle settle = simpleRequest.getO(Settle.class);
        // 结算表冻结数量等
        reduceItemsAndRebates(simpleRequest);
        // 重新走新增流程
        return save(settle);
    }

    /*
     * 还原结算表冻结数量, 返利明细冻结金额等
     * */
    private Settle reduceItemsAndRebates(SimpleRequest simpleRequest) {
        Settle settle = findValidSettle(simpleRequest);
        // 回写返利明细冻结 还原账款账
        settle.getSettleItems().forEach((item) -> invoiceService.decreaseInvoiceItemFreezeQty(item.getInvoiceItem(), item.getActualQty() * -1));
        // 增加未清,减少冻结
        transaction(settle, "current_unsettled", settle.getSettleAmount() + settle.getRebateAmount(), "current_freeze", -1 * (settle.getSettleAmount() + settle.getRebateAmount()));
        // 回写返利明细冻结 还原返利账
        settle.getSettleRebates().forEach(
                (item) -> {
                    IncomeItem incomeItem = item.getIncomeItem();
                    incomeItem.changeFreezeAmount(-1 * item.getActualAmount());
                    incomeItem.updateDone();
                }
        );
        double rebatesAmount = getSettleRebatesAmount(settle);
        if (rebatesAmount > 0) {
            // 增加返利未清，减少返利冻结
            transaction(settle, "discount_unsettled", rebatesAmount, "discount_freeze", -1 * rebatesAmount);
        }
        return settle;
    }

    /*
     * 查找有效订单
     * */
    private Settle findValidSettle(SimpleRequest simpleRequest) {
        Settle settle = settleRepository.findOne((String) simpleRequest.get("id"));
        if (settle == null) throw new RuntimeException("结算单不存在：" + (String) simpleRequest.get("id"));
        if (settle.getVersion() != (int) simpleRequest.get("version")) throw new RuntimeException("订单状态已改变,请重新查询订单！");
        return settle;
    }

    /*
     * 删除
     * */
    public String delete(SimpleRequest simpleRequest) {
        Settle settle = reduceItemsAndRebates(simpleRequest);
//        settleItemRepository.deleteAllBySettleId(settle.getId());
//        settle.setSettleItems(null);
        settleRepository.delete(settle);
        return "删除成功！";
    }

    /**
     * 审核 保存发票申请单信息
     * @param simpleRequest
     * @return
     */
    public Settle approveControls(SimpleRequest simpleRequest) {
        // 检验订单有效信息
        Settle settle = findValidSettle(simpleRequest);
        Settle settleNew = simpleRequest.getO(Settle.class);
        // 税票主表信息
        TaxBill taxBill = settleNew.getTaxBill();
        taxBill.getTaxBillItems().forEach(item -> item.setType(businessAttributeValueRepository.findOne(item.getType().getId())));
        if (StringUtil.isEmpty(taxBill.getCreator())) {
            taxBill.setCreator(simpleRequest.getPersonId());
        }
        taxBill.setInnerBusinessType(TaxBill.class.getSimpleName());
        taxBill.setPartner(settle.getPartner());
        taxBill.setCompany(settle.getCompany());
        taxBill.setCooperation(settle.getCooperation());
        taxBill.setDept(settle.getDept());
        taxBill.setState("sent");
        taxBill.computeAmt();   // 计算相关金额字段
        if (taxBill.getTaxBillItems().stream().noneMatch(taxBillItem -> taxBillItem.getType().getCode().equals("return"))) {
            if (Arith.compare(taxBill.getAmtTotal(), settle.getSettleAmount(), 2) != 0) {
                throw new RuntimeException("税票金额总额不等于本期结算的结算总额请核对！！！");
            }
            if (Arith.compare(taxBill.getRebateAmount(), settle.getRebateAmount(), 2) != 0) {
                throw new RuntimeException("发票折扣总计不等于返利金额！");
            }

            // 允许有 0.5 的误差，并且记录误差值
            settle.setSettleTaxDiff(Arith.round(Math.abs(taxBill.getAmtTax() - settle.getSettleTax()), 2));
            if (settle.getSettleTaxDiff() > 0.5) {
                throw new RuntimeException("税金总额不等于本期结算的结算税金总额请核对！！！");
            }

        }
        taxBill.setSettle(settle);
        taxBill = taxBillRepository.saveAndFlush(taxBill);
        settle.setTaxBill(taxBill);
        settle.setPaymentAmount(taxBill.getAmtTotal());
        settle.setState("approve");
        settle.setApproveUID(simpleRequest.getPerson());
        settle.setApproveDate(new Date());

        return createSettle(settle);
    }

    /**
     *
     * @param simpleRequest
     * @return
     */
    public Settle approveCustomer(SimpleRequest simpleRequest) {
        // 检验订单有效信息
        Settle settle = findValidSettle(simpleRequest);
        Settle settleNew = simpleRequest.getO(Settle.class);
        // 税票主表信息
        TaxBill taxBill = settleNew.getTaxBill();
        taxBill.getTaxBillItems().forEach(item -> item.setType(businessAttributeValueRepository.findOne(item.getType().getId())));
        taxBill.setCreator(simpleRequest.getPersonId());
        taxBill.setInnerBusinessType(TaxBill.class.getSimpleName());
        taxBill.setPartner(settle.getPartner());
        taxBill.setCompany(settle.getCompany());
        taxBill.setCooperation(settle.getCooperation());
        taxBill.setDept(settle.getDept());
        taxBill.setState("sent");
        taxBill.computeAmt();   // 计算相关金额字段
        if (taxBill.getTaxBillItems().stream().noneMatch(taxBillItem -> taxBillItem.getType().getCode().equals("return"))) {
            if (Arith.compare(taxBill.getAmtTotal(), settle.getSettleAmount(), 2) != 0) {
                throw new RuntimeException("税票金额总额不等于本期结算的结算总额请核对！！！");
            }
            if (Arith.compare(taxBill.getRebateAmount(), settle.getRebateAmount(), 2) != 0) {
                throw new RuntimeException("发票折扣总计不等于返利金额！");
            }

            // 允许有 0.5 的误差，并且记录误差值
            settle.setSettleTaxDiff(Arith.round(Math.abs(taxBill.getAmtTax() - settle.getSettleTax()), 2));
            if (settle.getSettleTaxDiff() > 0.5) {
                throw new RuntimeException("税金总额不等于本期结算的结算税金总额请核对！！！");
            }

        }
        taxBill.setSettle(settle);
        taxBill = taxBillRepository.saveAndFlush(taxBill);
        settle.setTaxBill(taxBill);
        settle.setPaymentAmount(taxBill.getAmtTotal());
        settle.setState("approve");
        settle.setApproveUID(simpleRequest.getPerson());
        settle.setApproveDate(new Date());

        return createSettle(settle);
    }

    /**
     * 操作页面审核
     * @param simpleRequest
     * @return
     */
    public Settle approve(SimpleRequest simpleRequest) {
        // 检验订单有效信息
        Settle settle = findValidSettle(simpleRequest);
        if (settle.getSettleItems().isEmpty()) throw new RuntimeException("结算单明细数据有误请检查");
        settle.setState("approve");
        settle.setApproveUID(simpleRequest.getPerson());
        settle.setApproveDate(new Date());

        return createSettle(settle);
    }

    /**
     * 结算单执行
     * @param simpleRequest
     * @return
     */
    public Settle executive(SimpleRequest simpleRequest) {
        Settle settle = findValidSettle(simpleRequest);
        if (settle.getSettleItems().isEmpty()) throw new RuntimeException("结算单明细数据有误请检查");

        settle.setState("executive");
        settle.setExecutiveDate(new Date());
        settle.setExecutiveUID(simpleRequest.getPerson());

        // 明细表处理
        settle.getSettleItems().forEach(
                settleItem -> {
                    // 增加完成数量
                    invoiceService.addInvoiceItemSettledQty(settleItem.getInvoiceItem(), settleItem.getActualQty());
                    // 减少冻结数量
                    invoiceService.decreaseInvoiceItemFreezeQty(settleItem.getInvoiceItem(), settleItem.getActualQty() * -1);
                });

        // 返利明细回写
        settle.getSettleRebates().forEach(
                settleRebate -> {
                    IncomeItem incomeItem = settleRebate.getIncomeItem();
                    // 减少冻结金额
                    incomeItem.changeFreezeAmount(-1 * settleRebate.getActualAmount());
                    // 增加收款金额
                    incomeItem.changeRecoveryAmount(settleRebate.getActualAmount());
                });

        return settleRepository.saveAndFlush(settle);
    }


    /**
     * 客户结算单执行
     * @param simpleRequest
     * @return
     */
    public Settle executiveCustomer(SimpleRequest simpleRequest) {
        Settle settle = findValidSettle(simpleRequest);
        settle.setState("executive");
        settle.setExecutiveDate(new Date());
        settle.setExecutiveUID(simpleRequest.getPerson());

        // 发票登记单写审核人、审核时间
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", settle.getId()));
        if (!CollectionUtils.isEmpty(taxBills)){
            taxBills.forEach(item -> {
                item.setApproveDate(new Date());
                item.setApproveUID(settle.getApproveUID());
                item.setState("approve");
                taxBillRepository.saveAndFlush(item);
            });
        }
        // TODO 税票登记修改
//        TaxBill taxBill = settle.getTaxBill();
//        taxBill.setApproveDate(new Date());
//        taxBill.setApproveUID(simpleRequest.getPerson());
//        taxBill.setState("approve");

        // 明细表处理
        settle.getSettleItems().forEach(
                settleItem -> {
                    // 增加完成数量
                    invoiceService.addInvoiceItemSettledQty(settleItem.getInvoiceItem(), settleItem.getActualQty());
                    // 减少冻结数量
                    invoiceService.decreaseInvoiceItemFreezeQty(settleItem.getInvoiceItem(), settleItem.getActualQty() * -1);
                });

        // 返利明细回写
        settle.getSettleRebates().forEach(
                settleRebate -> {
                    IncomeItem incomeItem = settleRebate.getIncomeItem();
                    // 减少冻结金额
                    incomeItem.changeFreezeAmount(-1 * settleRebate.getActualAmount());
                    // 增加收款金额
                    incomeItem.changeRecoveryAmount(settleRebate.getActualAmount());
                });

        // 这里为主表的返利金额
        if (!CollectionUtils.isEmpty(taxBills)) {
            taxBills.forEach(taxBill -> {
                if (taxBill.getRebateAmount() > 0) {
                    // 减少返利冻结，增加返利已清
                    transaction(taxBill, "discount_freeze", -1 * settle.getRebateAmount(), "discount_settled", settle.getRebateAmount());
                    // 减少冻结,增加已到票,增加已清项
                    transaction(taxBill, "current_freeze", -1 * (taxBill.getAmtTotal() + taxBill.getRebateAmount()), "current_invoiced", taxBill.getAmtTotal(), "current_settled", taxBill.getRebateAmount());
                } else {
                    // 减少冻结,增加已到票
                    transaction(taxBill, "current_freeze", -1 * taxBill.getAmtTotal(), "current_invoiced", taxBill.getAmtTotal());
                }

                // 增加 invoice_unsettled 税票:未清项
                ledgerService.charge(taxBill.getCompany(), taxBill.getDept(), taxBill.getPartner(),
                        taxBill.getCooperation().getInventoryMode(), taxBill.getCooperation().getAccountingMethod(),
                        taxBill.getId(), taxBill.getInnerBusinessType(), new Date(), "invoice_unsettled", taxBill.getAmtTotal());
            });
        }
        return settleRepository.saveAndFlush(settle);
    }

    /**
     * 结算单自动审核
     * @param simpleRequest
     * @return
     */
    public Settle autoApprove(SimpleRequest simpleRequest) {
        String id = simpleRequest.get("id");
        Settle settle = settleRepository.findOne(id);
        if (settle.getSettleItems().isEmpty()) throw new RuntimeException("结算单明细数据有误请检查");

        // 主表数据
        TaxBill taxBill = new TaxBill();
        taxBill.setPartner(settle.getPartner());
        taxBill.setCompany(settle.getCompany());
        taxBill.setCooperation(settle.getCooperation());
        taxBill.setDept(settle.getDept());
        taxBill.setState("sent");
        taxBill.setSettle(settle);  // 关联 settle

        // 明细数据
        TaxBillItem taxBillItem = new TaxBillItem();
        BusinessAttributeValue type = new BusinessAttributeValue();
        type.setId("TaxBillItem.type.defalut");
        taxBillItem.setType(type);
        taxBillItem.setCode("99999999");
        taxBillItem.setAmount(settle.getSettleAmount());
        taxBillItem.setTaxAmount(settle.getSettleTax());
        taxBillItem.setNote("自动生成");
        taxBillItem.setTaxReturnDate(new Date());
        Set<TaxBillItem> taxBillItemSet = new HashSet<>();
        taxBillItemSet.add(taxBillItem);
        taxBill.setTaxBillItems(taxBillItemSet);

        taxBillRepository.save(taxBill);
        return settle;
    }


    /**
     * 通过 settle 创建 [PaymentRegister]付款登记表
     * @param request Settle
     * @return PaymentRegister
     */
    public PaymentRegister settleFlowPaymentRegister(SimpleRequest request) {
        String id = request.get("id");
        Settle settle = settleRepository.findOne(id);

        /* 付款登记 */
        PaymentRegister paymentRegister = new PaymentRegister();
        // 主表信息
        paymentRegister.setCompany(settle.getCompany());
        paymentRegister.setPartner(settle.getPartner());
        paymentRegister.setDept(settle.getDept());

        paymentRegister.setCooperation(settle.getCooperation());
        paymentRegister.setOperator(settle.getOperator());
        paymentRegister.setOrigin(settle.getId());
        paymentRegister.setOriginBusinessName(Settle.class.getSimpleName());
        paymentRegister.setScene(settle.getScene());

        paymentRegister.setState("sent");
        paymentRegister.setNote("外采场景流自动生成");

        // 明细表信息
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", settle.getId()));
        if (!CollectionUtils.isEmpty(taxBills)) {
            taxBills.forEach(taxBill -> {
                Set<PaymentRegisterItem> paymentRegisterItemSet = taxBill.getTaxBillItems().stream()
                        .map(taxBillItem -> {
                            PaymentRegisterItem paymentRegisterItem = new PaymentRegisterItem();
                            paymentRegisterItem.setTaxBillItem(taxBillItem);
                            paymentRegisterItem.setAmount(taxBillItem.getAmount());
                            paymentRegisterItem.setPayableAmount(taxBillItem.getAmount());
                            return paymentRegisterItem;
                        }).collect(Collectors.toSet());
                paymentRegister.setPaymentRegisterItem(paymentRegisterItemSet);
            });
        }
        return paymentRegister;
    }

    /**
     * 结算单申请开票
     * @param simpleRequest
     * @return
     */
    public Map applyInvoice(SimpleRequest simpleRequest) {
        JSONObject info = simpleRequest.getJO().getJSONObject("info");
        Settle settle = settleRepository.findOne(info.getString("id"));
        List<Map<String, Object>> maps = settleItemRepository.findAmountBySettleId(settle.getId());
        if (maps == null || maps.isEmpty()) throw new RuntimeException("该单目前不可申请开票，请检查该单结算金额是否已做收款登记");
        double payAmount = maps.stream().mapToDouble(value -> (double) value.get("payAmount")).sum();
        double totalAmount = Double.parseDouble(maps.get(0).get("totalAmount").toString());
        if (totalAmount - payAmount > 0.0001) {
            throw new RuntimeException("该单目前不可申请开票，请检查该单结算金额是否已做收款登记");
        }
        // 生成发票申请明细
        List<Map> item = new ArrayList<>();
        settle.getSettleItems().forEach(settleItem -> {
            Product product = settleItem.getProduct();
            item.add(MapUtil.mapper(
                    "origin", settle.getId(),
                    "itemId", settleItem.getId(),
                    "sourceBusinessTypeName", settleItem.getOriginBusinessType(),
                    "originBusinessTypeName", "结算",
                    "source", settleItem.getOrigin(),
                    "sourceItemId", settleItem.getId(),
                    "carLicense", settleItem.getCarLicense(),
                    "actualInvoicedAmount", settleItem.getAmount(),
                    "priceTotal", settleItem.getAmount(),
                    "discountAmount", settleItem.getDiscount(),
                    "productQty", settleItem.getActualQty(),
                    "product", MapUtil.mapped(product),
                    "uom", MapUtil.mapped(settleItem.getUom()),
                    "tax", MapUtil.mapped(settleItem.getTax()),
                    "specifications", product.getSpecifications(),
                    "model", product.getModel(),
                    "serveType", MapUtil.mapped(product.getServeType()),
                    "productGroup", MapUtil.mapped(product.getProductGroup())

            ));
        });

        //创建发票申请单-直营
        Object invoiceApply = invoke("direct.invoiceApply.create",
                MapUtil.mapper(
                        "saleCategory", "settle",
                        "invoiceCompany", MapUtil.mapped(settle.getCompany()),
                        "company", MapUtil.mapped(settle.getCompany()),
                        "invoiceType", MapUtil.mapped(info.get("invoiceType")),
                        "buyerName", info.getString("buyerName"),
                        "buyerInfo", info.getString("buyerInfo"),
                        "buyerTaxNumber", info.getString("buyerTaxNumber"),
                        "buyerBankInfo", info.getString("buyerBankInfo"),
                        "email", info.getString("email"),
                        "totalSaleAmount", settle.getSettleAmount(),
                        "invoicedAmount", settle.getSettleAmount(),
                        "actualInvoicedAmount", settle.getSettleAmount(),
                        "invoiceApplyItems", item
                ));
        invoiceApply = invoke("direct.invoiceApply.apply", invoiceApply);
        Map map = MapUtil.mapped(invoiceApply);
        Map settleMap = MapUtil.mapped(settleRepository.findOne(settle.getId()));
        settleMap.put("invoiceApplyId", map.get("id"));
        return settleMap;
    }

    /**
     * 获取返利结算明细累计结算金额
     * 主表返利金额 = 返利明细表结算金额之和 + 商品明细表结算金额之和
     * @param settle
     * @return
     */
    private double getSettleRebatesAmount(Settle settle) {
        return settle.getSettleRebates().stream().mapToDouble(SettleRebate::getActualAmount).sum();
    }

    /**
     * 写入总账
     * @param settle
     * @param items
     */
    private void transaction(Settle settle, Object... items) {
        ledgerService.transaction(settle.getCompany(), settle.getDept(), settle.getPartner(),
                settle.getCooperation().getInventoryMode(), settle.getCooperation().getAccountingMethod(),
                settle.getId(), settle.getInnerBusinessType(), new Date(), items);
    }

    /**
     * 写入总账
     * @param taxBill
     * @param items
     */
    private void transaction(TaxBill taxBill, Object... items) {
        ledgerService.transaction(taxBill.getCompany(), taxBill.getDept(), taxBill.getPartner(),
                taxBill.getCooperation().getInventoryMode(), taxBill.getCooperation().getAccountingMethod(),
                taxBill.getId(), taxBill.getInnerBusinessType(), new Date(), items);
    }

    /**
     * 根据单号查找明细
     * @param simpleRequest
     * @return
     */
    public List<Map<String, Object>> findSettleItemsBySettleId(SimpleRequest simpleRequest) {
        String settleId = simpleRequest.get("settleId");
        return settleItemRepository.findAllBySettleId(settleId);
    }

    /**
     * 付款单界面查询税票
     * @param request
     * @return
     */
    private List<TaxBillItem> findForPurchasePayment(SimpleRequest request) {
        String companyId = request.get("companyId");
        String partnerId = request.get("partnerId");
        String cooperationId = request.get("cooperationId");
        return taxBillItemRepository.findTaxBillItemForPaymentForForm(companyId, partnerId, cooperationId);
    }

    /**
     * 付款单界面查询税票
     * @param request
     * @return
     */
    private List<TaxBillItem> findForSalPayment(SimpleRequest request) {
        String companyId = request.get("companyId");
        String partnerId = request.get("partnerId");
        String cooperationId = request.get("cooperationId");
        return taxBillItemRepository.findTaxBillItemForPayment(companyId, partnerId, cooperationId);
    }

    /**
     * 获取结算数据
     * @param request
     * @return
     */
    private Map getSettlementData(SimpleRequest request) {
        String contractId = request.get("contractId");
        Contract contract = SpringManager.getBean(ContractRepository.class).findOne(contractId);
        String partnerId = contract.getPartner().getId();
        String cooperationId = contract.getCooperation().getId();
        String companyId = contract.getCompany().getId();

        //库存金额
        double inventoryAmount = SpringManager.getBean(InventoryBatchRepository.class).findStandingInventoryAmount(companyId, partnerId, cooperationId);
        //获取总账
        Map<String, Double> ledger = ledgerService.summary(
                MapUtil.mapper("company.id", companyId, "partner.id", partnerId, "inventoryMode", contract.getCooperation().getInventoryMode()));
        //应付账款
        Double accountPayable = ledger.get("current_unsettled") == null ? 0.0 : ledger.get("current_unsettled");
        //预付未清项
        Double prepaidBalance = ledger.get("prepayment_unsettled") == null ? 0.0 : ledger.get("prepayment_unsettled");
        //返利金额
        List<IncomeItem> incomeItemList = SpringManager.getBean(IncomeItemRepository.class).findAllByDoneAndIncome_StateAndIncome_Contract_Id(false, "approve", contractId);
        Double rebateTotal = incomeItemList.stream().map(incomeItem -> incomeItem.getIncomeAmount() - incomeItem.getFreezeAmount() - incomeItem.getRecoveryAmount()).reduce(Arith::add).orElse(0.0);
        Date billingPeriod = null;
        //到期应付
        Double duePayable = 0.0;
        if (contract.getSettlementType().getCode().equals("month")) {
            billingPeriod = DateUtil.add(new Date(), Calendar.MONTH, -1);
        }
        if (contract.getSettlementType().getCode().equals("halfMonth")) {
            billingPeriod = DateUtil.add(new Date(), Calendar.DATE, -15);
        }
        if (contract.getSettlementType().getCode().equals("week")) {
            billingPeriod = DateUtil.add(new Date(), Calendar.DATE, -7);
        }
        if (billingPeriod != null) {
            duePayable = SpringManager.getBean(LedgerRepository.class).findLedger(contract.getCompany(), contract.getPartner(), contract.getCooperation().getInventoryMode(), "current_unsettled", billingPeriod)
                    .stream().map(Ledger::getAmount).reduce(Arith::add).orElse(0.0);
        }
        HashMap resultMap = new HashMap<>();
        resultMap.put("inventoryAmount", inventoryAmount);
        resultMap.put("accountPayable", accountPayable);
        resultMap.put("prepaidBalance", prepaidBalance);
        resultMap.put("rebateTotal", rebateTotal);
        resultMap.put("duePayable", duePayable);
        return resultMap;

    }

    /**
     * 客户结算单据-申请开票-页面数据封装
     * @param request
     * @return
     */
    public Map applyInvoicePageData(SimpleRequest request) {
        JSONObject jo = request.getJO();
        PartyRole partyRole = partyRoleRepository.findOne(jo.getString("code"));
        if (partyRole == null) throw new RuntimeException("无法查询到该客户！");
        Party party = partyRole.getParty();
        Map result = new HashMap<>();
        result.put("buyerTaxNumber", party.getCorporate().getUscc());
        result.put("buyerName", party.getInvoiceTitle());
        result.put("buyerInfo", party.getInvoiceAddress());
        result.put("buyerBankInfo", "开户行:" + party.getBank() + ",银行账户:" + party.getBankAccount());
        return result;
    }

    /**
     * 发票登记
     *
     * @param simpleRequest
     * @return
     */
    public Settle registration(SimpleRequest simpleRequest) {
        // 检验订单有效信息
        Settle settleNew = simpleRequest.getO(Settle.class);
        Settle settle = settleRepository.getOne(settleNew.getId());
        // 税票主表信息
        TaxBill taxBill = getTaxBill(settle, settleNew);
        settle.setTaxBill(taxBill);
        settle.setPaymentAmount(taxBill.getAmtTotal());

        AtomicReference<Double> taxAmount = new AtomicReference<>((double) 0);
        List<TaxBill> taxBills = taxBillRepository.findAll(JpaDsl.toCriteriaByEq("settle.id", settle.getId()));
        if (!CollectionUtils.isEmpty(taxBills)){
            taxBills.forEach(item -> {
                taxAmount.updateAndGet(v -> new Double((double) (v + item.getTaxBillItems().stream().mapToDouble(TaxBillItem::getAmount).sum())));
            });
        }
        double settleSum = settle.getSettleAmount();//结算金额
        //double taxAmount = settle.getTaxBill().getTaxBillItems().stream().mapToDouble(TaxBillItem::getAmount).sum();//发票的金额 + 税金
        if (settleSum == taxAmount.get()) {
            settle.setReceiptState("already");
        } else if (settleSum != taxAmount.get() && taxAmount.get() > 0) {
            settle.setReceiptState("section");
        }
        return createSettle(settle);
    }

    private TaxBill getTaxBill(Settle settle, Settle settleNew) {
        TaxBill taxBill = settleNew.getTaxBill();
        taxBill.setCreator(settle.getCreator());
        taxBill.setInnerBusinessType(TaxBill.class.getSimpleName());
        taxBill.setPartner(settleNew.getPartner());
        taxBill.setCompany(settleNew.getCompany());
        taxBill.setCooperation(settleNew.getCooperation());
        taxBill.setDept(settleNew.getDept());
        taxBill.setState("sent");
        taxBill.setTaxBillItems(settleNew.getTaxBill().getTaxBillItems());
        if (Objects.nonNull(settleNew.getOrigin())) {
            List<Map<String, String>> platformSettle = settleRepository.getPlatformSettle(settleNew.getOrigin());
            taxBill.setPaymentMonth(platformSettle.get(0).get("paymentMonth") == null ? null : platformSettle.get(0).get("paymentMonth"));
        }

        taxBill.computeAmt();   // 计算相关金额字段
        taxBill.getTaxBillItems().stream().forEach(v -> {
            if (StringUtils.isEmpty(v.getId())) return;
            TaxBillItem taxBillItem = taxBillItemRepository.getOne(v.getId());
            boolean sum = v.getFreezeAmount() + v.getActualAmount() != 0 && (taxBillItem.getAmount() != v.getAmount() || taxBillItem.getTaxAmount() != v.getTaxAmount()
                    || taxBillItem.getRebateAmount() != v.getRebateAmount() || taxBillItem.getTaxAmount() != v.getTaxAmount()
                    || taxBillItem.getExpenseAmount() != v.getExpenseAmount());
            if (sum) throw new RuntimeException("该发票金额，" + v.getAmount() + "已排款或付款，不允许调整！！！");
        });

        if ((taxBill.getAmtTotal() > 0 && Arith.round(taxBill.getAmtTotal(), 2) > Arith.round(settle.getSettleAmount(), 2))
                || (taxBill.getAmtTotal() < 0 && Arith.round(taxBill.getAmtTotal(), 2) < Arith.round(settle.getSettleAmount(), 2)))
            throw new RuntimeException("发票金额不能大于本结算单的结算金额，请核对！！！");

        if ((taxBill.getAmtTotal() > 0 && Arith.round(taxBill.getAmtTax(), 2) > Arith.round((settle.getSettleTax() + 0.5), 2))
                || (taxBill.getAmtTotal() < 0 && Arith.round(taxBill.getAmtTax(), 2) < Arith.round((settle.getSettleTax() - 0.5), 2)))
            throw new RuntimeException("税金总额不能大于本结算单的结算税金，请核对！！！");

        if (taxBill.getAmtTotal() == settle.getSettleAmount() && (settle.getSettleTaxDiff() > 0.5 || settle.getSettleTaxDiff() < -0.5))
            throw new RuntimeException("发票金额等于结算金额，但税金总额不等于结算税金！！");

        if (Arith.round(taxBill.getAmtTotal(), 2) == Arith.round(settle.getSettleAmount(), 2)) {
            settle.setSettleTaxDiff(Arith.round((taxBill.getAmtTax() + taxBill.getTaxBillItems().stream().mapToDouble(TaxBillItem::getTaxLoss).sum()) - settle.getSettleTax(), 2));
        }

        taxBill.setSettle(settleNew);
        return getBill(taxBillRepository.saveAndFlush(taxBill), settleNew);
    }

    private TaxBill getBill(TaxBill taxBill, Settle settle) {
        List<Map<String, String>> platformSettle = settleRepository.getPlatformSettle(settle.getOrigin());
        Map<String, String> map = new HashMap<>();
        if (taxBill.getAmtTotal() == settle.getSettleAmount()) {
            map.put("state", "already");
        } else {
            map.put("state", "section");
        }
        platformSettle.add(map);
        invoke("platform.settle.settle", MapUtil.mapped(platformSettle));

        // 发票登记单写审核人、审核时间
        taxBill.setApproveDate(new Date());
        taxBill.setApproveUID(settle.getApproveUID());
        taxBill.setState("approve");

        // 这里为主表的返利金额
        if (taxBill.getRebateAmount() > 0) {
            // 减少返利冻结，增加返利已清
            transaction(taxBill, "discount_freeze", -1 * settle.getRebateAmount(), "discount_settled", settle.getRebateAmount());
            // 减少冻结,增加已到票,增加已清项
            transaction(taxBill, "current_freeze", -1 * (taxBill.getAmtTotal() + taxBill.getRebateAmount()), "current_invoiced", taxBill.getAmtTotal(), "current_settled", taxBill.getRebateAmount());
        } else {
            // 减少冻结,增加已到票
            transaction(taxBill, "current_freeze", -1 * taxBill.getAmtTotal(), "current_invoiced", taxBill.getAmtTotal());
        }

        // 增加 invoice_unsettled 税票:未清项
        ledgerService.charge(taxBill.getCompany(), taxBill.getDept(), taxBill.getPartner(),
                taxBill.getCooperation().getInventoryMode(), taxBill.getCooperation().getAccountingMethod(),
                taxBill.getId(), taxBill.getInnerBusinessType(), new Date(), "invoice_unsettled", taxBill.getAmtTotal());
        return taxBillRepository.saveAndFlush(taxBill);
    }

}

